package org.icop.frame.search.virtualdoc;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.icop.frame.search.Searcher;
import org.icop.utils.RPSTCache;
import org.icop.virtdoc.VirtualDoc;
import org.jbpt.algo.tree.rpst.IRPST;
import org.jbpt.algo.tree.rpst.IRPSTNode;
import org.jbpt.algo.tree.tctree.TCType;
import org.jbpt.graph.abs.IDirectedEdge;
import org.jbpt.graph.abs.IDirectedGraph;
import org.jbpt.hypergraph.abs.IVertex;

/**
 * This searcher looks for viable mappings based on components that are derived
 * by the RPSTree decomposition. Similarities are evaluated based on virtual documents
 * that are build from all nodes of a component.
 * 
 * First, we created documents for all single nodes in the graph.
 * Second, starting from the leaves of the tree, all components upwards in the tree till level 
 * componentDepth are evaluated. Here, setting componentDepth to 0 yields only the trivial 
 * components containing a single node that have been considered in the first step.
 * 
 * For all graph nodes contained in a certain component we build a virtual doc. Based 
 * thereon, a VSM is initialized with all documents. Afterwards, all possible doc
 * combinations are scored. If the score is higher than the cutoff value, the respective
 * mapping is part of the result set.
 * 
 * @author matthias.weidlich
 */
public class StandardVirtualDocForComponentSearcher<G extends IDirectedGraph<E,V>, E extends IDirectedEdge<V>, V extends IVertex> extends AbstractVirtualDocSearcher<G, E, V> implements Searcher<G, E, V> {

	protected int componentDepth;
	
	public StandardVirtualDocForComponentSearcher(int componentDepth, double cutoff, boolean doWrapup, boolean noNtoMMatches, boolean no1toNMatches) {
		super(cutoff,doWrapup, noNtoMMatches, no1toNMatches);
		this.componentDepth = componentDepth;
	}
		
	@Override
	protected void createDocsForNodeSets(G g, Map<Set<V>, VirtualDoc> nodesToVirtualDoc, Set<VirtualDoc> docs) {
		@SuppressWarnings("unchecked")
		IRPST<E, V> rpsTree = (IRPST<E, V>) RPSTCache.getInstance().getRPSTForGraph((IDirectedGraph<IDirectedEdge<IVertex>, IVertex>) g);

		Set<Set<V>> nodeSets = new HashSet<>();		
		
		for (int i = 0; i < this.componentDepth; i++) {
			for (IRPSTNode<E, V> leaf : rpsTree.getRPSTNodes(TCType.TRIVIAL)) {
				IRPSTNode<E, V> componentNode = getComponentInDepthForNode(rpsTree, leaf, i, 0);
				
				Set<V> nodes = new HashSet<>();
				for (IRPSTNode<E, V> treeNode : rpsTree.getChildrenRecursively(componentNode)) {
					if (treeNode.getType().equals(TCType.TRIVIAL) && treeNode.getEntry() != null) {
						if (super.nodesInGraph.get(g).contains(treeNode.getEntry()))
							nodes.add(treeNode.getEntry());
					}
				}
				if (!nodes.isEmpty())
					nodeSets.add(new HashSet<>(nodes));
				
				nodes.remove(componentNode.getEntry());
				nodes.remove(componentNode.getExit());
				if (!nodes.isEmpty())
					nodeSets.add(new HashSet<>(nodes));
			}
			i++;
		}
		
		/*
		 * Generate docs for sets of nodes 
		 */
		for (Set<V> nodeSet : nodeSets) {
			if (nodesToVirtualDoc.containsKey(nodeSet))
				continue;
			
			VirtualDoc doc = getVirtualDocForNodes(g, nodeSet);
			nodesToVirtualDoc.put(nodeSet, doc);
			docs.add(doc);
		}
		
	}
		
	/**
	 * Starting at a certain node, the function traverses the RPSTree upwards till
	 * the specified depth is reached and returns the respective node.
	 * 
	 * Note that the function might return null in case the root node is reached.
	 * 
	 * @param treeNode, the node from which we start the traversal
	 * @param depth, the depth as seen from the leaves of the tree
	 * @param curDepth
	 * @return the treeNode upwards at distance depth starting from the node given as first parameter
	 */
	protected IRPSTNode<E, V> getComponentInDepthForNode(IRPST<E, V> rpsTree, IRPSTNode<E, V> treeNode, int depth, int curDepth) {

		if ((treeNode == null) || (depth == curDepth))
			return treeNode;
		
		return getComponentInDepthForNode(rpsTree, rpsTree.getParent(treeNode), depth, curDepth++);
	}
}
